package com.dili.dd.gpi.service.impl;

import com.dili.dd.gpi.domain.UidNameSpace;
import com.dili.dd.gpi.enums.ApplyType;
import com.dili.dd.gpi.enums.PrefixType;
import com.dili.dd.gpi.exception.BusinessException;
import com.dili.dd.gpi.mapper.ExtUidNameSpaceMapper;
import com.dili.dd.gpi.mapper.UidNameSpaceMapper;
import com.dili.dd.gpi.service.UidNameSpaceService;
import com.dili.dd.gpi.util.StringUtils;
import com.dili.dd.gpi.util.spring.DataSourceLookupKey;
import com.dili.dd.gpi.util.spring.RequireDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.text.MessageFormat;
import java.util.*;

/**
 * Created by zhaoYuan on 2015/3/24.
 */
@Service("dataBaseGenerator")
@SuppressWarnings("SpringJavaAutowiringInspection")
public class UidNameSpaceServiceImpl implements UidNameSpaceService {

    private static Logger log = LoggerFactory.getLogger(UidNameSpaceService.class);

    @Resource
    private UidNameSpaceMapper uidNameSpaceMapper;

    @Resource
    private ExtUidNameSpaceMapper extUidNameSpaceMapper;

    private static List<String> reservedNameSpace = new ArrayList<>();

    public UidNameSpaceServiceImpl(){
        reservedNameSpace.add(UN_PREFIX);
        //大写的Z，作为在保留空间已经写满后。作为前缀来标识，无自定义前缀的id；不限长
        for (char c = 'A';c <= 'Y';c++){
            reservedNameSpace.add(String.valueOf(c));
        }
        // 小写的z作为 ，自动生成页面系统分配的唯一标注的前缀。不限长度
        for (char c = 'a';c <= 'z';c++){
            reservedNameSpace.add(String.valueOf(c));
        }
    }

    @Override
    @RequireDataSource(key = DataSourceLookupKey.GPI_DATASOURCE)
    @Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
    public UidNameSpace get(Integer id) {
        return uidNameSpaceMapper.selectByPrimaryKey(id);
    }

    @Override
    @RequireDataSource(key = DataSourceLookupKey.GPI_DATASOURCE)
    @Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
    public UidNameSpace getByPrefix(PrefixType prefixType, String prefix, String level) {
        List<UidNameSpace> uidNameSpaces = extUidNameSpaceMapper.getByPrefix(prefix,prefixType, level);
        return uidNameSpaces == null || uidNameSpaces.isEmpty() ? null : uidNameSpaces.get(0);
    }

    @RequireDataSource(key = DataSourceLookupKey.GPI_DATASOURCE)
    @Transactional(readOnly = false,propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    private void add( PrefixType prefixType,String prefix,int currentValue) {
        UidNameSpace uidNameSpace = new UidNameSpace();
        Date now = new Date();
        uidNameSpace.setCreateTime(now);
        uidNameSpace.setUpdateTime(now);
        uidNameSpace.setType(prefixType);
        uidNameSpace.setNameSpace(prefix);
        uidNameSpace.setCurrentValue(currentValue);

        uidNameSpaceMapper.insert(uidNameSpace);
    }

    @Override
    @RequireDataSource(key = DataSourceLookupKey.GPI_DATASOURCE)
    @Transactional(readOnly = false,propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    public String generateUID(PrefixType prefixType, String prefix, ApplyType applyType, String level) throws BusinessException {
        UidNameSpace uidNameSpace;
        Date now = new Date();
        //如果prefix为空，则使用无前缀，或者保留空间
        if (StringUtils.isNullOrEmpty(prefix)) {
            if(ApplyType.API.equals(applyType)){
                UidNameSpace apiCurrentReservedNameSpace = getApiCurrentReservedNameSpace(prefixType,level);
                if(apiCurrentReservedNameSpace == null){
                    uidNameSpace =  new UidNameSpace();
                    uidNameSpace.setNameSpace(API_DEFAULT_PREFIX);
                    uidNameSpace.setCurrentValue(UN_PREFIX_DEFAULT_VALUE);
                    uidNameSpace.setType(prefixType);
                    uidNameSpace.setLevel(level);
                    uidNameSpace.setCreateTime(now);
                    uidNameSpace.setUpdateTime(now);
                }else{
                    uidNameSpace = apiCurrentReservedNameSpace;
                }
            }else{
                UidNameSpace currentReservedNameSpace = getCurrentReservedNameSpace(prefixType,level);
                //如果currentReservedNameSpace为空，说明还没有申请过，那么新建无前缀id；
                String defaultPrefix = UN_PREFIX;
                if(currentReservedNameSpace == null){
                    uidNameSpace = new UidNameSpace();
                    uidNameSpace.setNameSpace(defaultPrefix);
                    uidNameSpace.setCurrentValue(UN_PREFIX_DEFAULT_VALUE);
                    uidNameSpace.setType(prefixType);
                    uidNameSpace.setLevel(level);
                    uidNameSpace.setCreateTime(now);
                    uidNameSpace.setUpdateTime(now);
                }else{
                    uidNameSpace = getNextReservedNameSpace(currentReservedNameSpace, level);
                }
            }
        }else{
            //如果不为空，则获取该 命名空间
            UidNameSpace byPrefix = getByPrefix(prefixType, prefix,level);
            //如果没有该命名空间，则新建一个
            if(byPrefix == null){
                uidNameSpace =  new UidNameSpace();
                uidNameSpace.setNameSpace(prefix);
                uidNameSpace.setCurrentValue(PREFIX_DEFAULT_VALUE);
                uidNameSpace.setType(prefixType);
                uidNameSpace.setLevel(level);
                uidNameSpace.setCreateTime(now);
                uidNameSpace.setUpdateTime(now);
            }else{
                //判断命名空间是否已满，如果已满，则抛出异常。
                if(byPrefix.getCurrentValue() >= PREFIX_MAX_VALUE){
                    throw new BusinessException(MessageFormat.format("前缀{0}下的gpi已经用完,请更换其他前缀", prefix));
                }else{
                    uidNameSpace = byPrefix;
                }
            }
        }

        uidNameSpace.setCurrentValue(uidNameSpace.getCurrentValue() + 1);
        uidNameSpace.setUpdateTime(now);

        if(uidNameSpace.getId() == null){
            uidNameSpaceMapper.insert(uidNameSpace);
        }else{
            uidNameSpaceMapper.updateByPrimaryKey(uidNameSpace);
        }
        return uidNameSpace.getNameSpace() + uidNameSpace.getCurrentValue();

    }

    /**
     * 获取当前的系统保留前缀，
     * @param prefixType
     * @param level
     * @return
     */
    private UidNameSpace getCurrentReservedNameSpace(PrefixType prefixType, String level){
        Map<String,Object> params = new HashMap<>();
        params.put("nameSpaces",reservedNameSpace);
        params.put("prefixType",prefixType);
        params.put("level",level);
        List<UidNameSpace> uidNameSpaces = extUidNameSpaceMapper.findReservedNameSpace(params);
        Collections.sort(uidNameSpaces, new Comparator<UidNameSpace>() {
            @Override
            public int compare(UidNameSpace o1, UidNameSpace o2) {
                if(StringUtils.isNullOrEmpty(o1.getNameSpace())){
                    return StringUtils.isNullOrEmpty(o2.getNameSpace()) ? 0 : 1;
                }
                if (StringUtils.isNullOrEmpty(o2.getNameSpace())) {
                    return -1;
                }
                return o2.getNameSpace().charAt(0) - o1.getNameSpace().charAt(0);
            }
        });
        return uidNameSpaces == null || uidNameSpaces.isEmpty() ? null : uidNameSpaces.get(0);
    }

    /**
     * 获取当前的系统保留前缀，
     * @param prefixType
     * @param level
     * @return
     */
    private UidNameSpace getApiCurrentReservedNameSpace(PrefixType prefixType, String level){
        List<UidNameSpace> uidNameSpaces = extUidNameSpaceMapper.getByPrefix("Z", prefixType,level);
        return uidNameSpaces == null || uidNameSpaces.isEmpty() ? null : uidNameSpaces.get(0);
    }

    private UidNameSpace getNextReservedNameSpace(UidNameSpace current, String level){
        UidNameSpace result = current;
        String prefix = current.getNameSpace();
        Date now = new Date();
        if(!"z".equalsIgnoreCase(prefix) && current.getCurrentValue() >= 999){
            result = new UidNameSpace();
            if(prefix.isEmpty()){
                result.setNameSpace("A");
            }else if("Y".equals(prefix)){
                result.setNameSpace("a");
            }else{
                char c = prefix.charAt(0);
                result.setNameSpace(String.valueOf((char)(c + 1)));
            }
            result.setCurrentValue(0);
            result.setType(current.getType());
            result.setLevel(level);
            result.setCreateTime(now);
            result.setUpdateTime(now);
        }
        return result;
    }
}
